package ch.subgate.pongwars;

import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class GameView extends SurfaceView implements SurfaceHolder.Callback {
	
	/*
	 * ---- Embedded GameThread class content ----
	 */
	
	public class GameThread extends Thread {
		
		/*
		 * ---- Thread related members ----
		 */
		
		/** SurfaceHolder gives access to the canvas */
		private final SurfaceHolder mSurfaceHolder;
		
		/** Variable to stop the threads execution */
		private boolean mIsRunning;

        /** Variable to distinguish between server and client */
        private boolean mIsServer;

        /** Streaming worker thread for bluetooth match */
        private StreamThread mStreamThread;
		
		/*
		 * ---- Metrics ----
		 */

        /** GameContext object is holding all the necessary metric data */
        private GameContext mGameContext;
		
		/*
		 * ---- Rendering objects ----
		 */

        private Renderer mRenderer;
		
		/*
		 * ---- Time measurement ----
		 */
		
		/** Time when the last frame was drawn */
		private long mLastFrame;
		
		/** Time consumed between two iterations of the thread */
		private float mDeltaTime;

		/** GameThread constructor */
		public GameThread(SurfaceHolder holder, Context context) {
			mSurfaceHolder = holder;
			
			// Initialize thread state
			mIsRunning = false;
            mStreamThread = null;

            // Instantiate GameContext
            mGameContext = new GameContext();
			
			// Initiate time measurement
			mLastFrame = System.nanoTime();
			mDeltaTime = 0.0f;

            // Create renderer
            mRenderer = new Renderer(context);
		} // Constructor
		
		@Override
		public void run() {
			Canvas canvas;
			while (mIsRunning) {

                if (mIsServer) {
                    // Get the time passed between the last cycle and this one
                    long currentFrame = System.nanoTime();
                    mDeltaTime = (currentFrame - mLastFrame) / 1000000000.0f;
                    mLastFrame = currentFrame;
                    mGameContext.update(mDeltaTime);
                } // if

                // Draw Game
                canvas = null;
                try {
                    canvas = mSurfaceHolder.lockCanvas();
                    synchronized (mSurfaceHolder) {
                        mRenderer.draw(canvas, mGameContext, mDeltaTime);
                    }
                }
                catch (Exception e) {
                    // do nothing about it
                }
                finally {
                    if (canvas != null) {
                        mSurfaceHolder.unlockCanvasAndPost(canvas);
                    }
                }

                // Check if ball has hit the back of the arena on either side
                mGameContext.checkGoals();

                // Check for win
                mGameContext.checkWin();
			} // while

            if (mStreamThread != null) {
                // stop streaming thread before dying
                mStreamThread.setIsRunning(false);
                try {
                    mStreamThread.join();
                } catch (InterruptedException e) {
                    // do nothing
                }
            }
		} // run
		
		public void setSurfaceSize(int width, int height) {
			synchronized (mSurfaceHolder) {
                mGameContext.initMetrics(width, height);
                mRenderer.init(mGameContext);
			} // synchronized
		} // setSurfaceSize
		
		public void setRunning(boolean running) {
			mIsRunning = running;
		} // setRunning

		public boolean handleTouchEvent(MotionEvent e) {
            int action = e.getActionMasked();
            for (int i = 0; i < e.getPointerCount(); i++) {
                float diX = GameHelper.getDICFromSC(mGameContext, e.getX(i));
                boolean down = true;
                switch (action) {
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_POINTER_UP:
                        down = false;
                        break;
                } // switch
                if (mGameContext.playerRed instanceof NetworkPlayer || mGameContext.playerRed instanceof AIPlayer) {
                    ((HumanPlayer)mGameContext.playerBlue).setX(diX);
                    ((HumanPlayer)mGameContext.playerBlue).setIsCharging(!down);
                } else if (mGameContext.playerBlue instanceof NetworkPlayer || mGameContext.playerBlue instanceof AIPlayer) {
                    ((HumanPlayer)mGameContext.playerRed).setX(diX);
                    ((HumanPlayer)mGameContext.playerRed).setIsCharging(!down);
                } else {
                    if (e.getY(i) < mGameContext.battlefieldTop + (0.5f * mGameContext.battlefieldHeight)) {
                        ((HumanPlayer)mGameContext.playerRed).setX(diX);
                        ((HumanPlayer)mGameContext.playerRed).setIsCharging(!down);
                    } else {
                        ((HumanPlayer)mGameContext.playerBlue).setX(diX);
                        ((HumanPlayer)mGameContext.playerBlue).setIsCharging(!down);
                    } // if
                } // if
            } // for
			return true;
		} // handleTouchEvent

        public void setGameMode(int mode, float paddleSize, boolean isServer) {
            mIsServer = isServer;
            mGameContext.initGameObjects(mode, paddleSize, isServer);
        } // setGameMode

        public void setSocket(BluetoothSocket socket) {
            mStreamThread = new StreamThread(socket, mGameContext, mIsServer);
            mStreamThread.setIsRunning(true);
            mStreamThread.start();
        } // setSocket
		
	} // GameThread
	
	/*
	 * ---- GameView class content ----
	 */
	
	private GameThread mThread;
	
	public GameView(Context context, AttributeSet attributes) {
		super(context, attributes);
		
		//Retrieve the surface holder and set the callback
		SurfaceHolder surfaceHolder = getHolder();
		surfaceHolder.addCallback(this);
		
		//Creating a thread without starting it yet
		mThread = new GameThread(surfaceHolder, context);
	} // Constructor
	
	@Override
	public boolean onTouchEvent(MotionEvent e) {
		return mThread.handleTouchEvent(e);
	}
	
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		mThread.setSurfaceSize(width, height);
	}
	
	@Override
	public void surfaceCreated(SurfaceHolder holder) { /* do nothing */ }
	
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) { /* do nothing */ }
	
	public GameThread getThread() {
		return mThread;
	}

} // GameView
